from fastapi import APIRouter, status, HTTPException, BackgroundTasks, Body, Depends
from fastapi_mail.errors import ConnectionErrors
from pydantic import EmailStr

from config import DOMAIN, RESET_PASSWORD_AUDIENCE, RESET_PASSWORD_EXPIRES
from helpers import send_mail
from .handlers import AuthHandler
from .models import User
from .schemas import UserCreateSchema, UserResponseSchema, UserLoginSchema

router = APIRouter()

auth_handler = AuthHandler()


@router.post('/register', status_code=status.HTTP_201_CREATED,
             response_model=UserResponseSchema)
async def register(user: UserCreateSchema, background_tasks: BackgroundTasks):
    data = user.dict()
    # 数据库中只保存哈希后的密码
    data['hashed_password'] = auth_handler.get_password_hash(data['password'])
    del data['password']
    db_user = await User.create(**data)  # 如果创建失败返回422 Error: Unprocessable Entity

    # 用户创建后发送激活邮件
    try:
        token = auth_handler.encode_token(db_user.id)
        body = f"""
        <p>感激注册{DOMAIN}，请点击下面链接激活您的账号</p> 
        <a href="http://{DOMAIN}/auth/verify/{token}">http://{DOMAIN}/auth/verify/{token}</a>
        """
        # https://fastapi.tiangolo.com/tutorial/background-tasks/
        background_tasks.add_task(send_mail, data['email'], body)
    except ConnectionErrors as e:
        # logger.error(e)
        raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=f'用户注册失败')
    return user


@router.get('/verify/{token}', status_code=status.HTTP_200_OK)
async def verify_token(token: str):
    user_id = auth_handler.decode_token(token)
    # 走到这，用户ID user_id一定存在
    db_user = await User.get_or_none(id=user_id)
    if db_user is None:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail='无效激活链接')

    if not db_user.is_active:  # 未激活情况下才执行下面代码，减少数据库操作
        db_user.is_active = True
        try:
            await db_user.save(update_fields=['is_active'])
        except Exception as e:
            # loggine.error(e)
            raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail='账号激活失败')


@router.post('/auth/jwt/create', status_code=status.HTTP_200_OK)
async def login(user: UserLoginSchema):
    # 如果注册邮箱不存在返回None
    db_user = await User.get_or_none(email=user.email)
    if not db_user.is_active:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=f'{user.email}该账号尚未激活，请求激活后再登录')

    # 比对用户传来的密码和数据库中的密码
    if db_user is None or (not auth_handler.verify_password(user.password, db_user.hashed_password)):
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED, detail='邮箱或者密码错误')

    token = auth_handler.encode_token(db_user.id)
    return {'access_token': token, 'token_type': 'bearer'}


@router.post('/forget-password', status_code=status.HTTP_202_ACCEPTED)
async def forget_password(background_tasks: BackgroundTasks, email: EmailStr):
    db_user = await User.get_or_none(email=email)
    if db_user is not None and db_user.is_active:
        token_data = {'sub': db_user.id, 'aud': RESET_PASSWORD_AUDIENCE}
        token = auth_handler.encode_token(**token_data, expires=RESET_PASSWORD_EXPIRES)
        try:
            body = f"""
            <p>请点击下面链接重置密码</p> 
            <a href="http://{DOMAIN}/auth/reset-password/{token}">http://{DOMAIN}/auth/reset-password/{token}</a>
            """
            # https://fastapi.tiangolo.com/tutorial/background-tasks/
            background_tasks.add_task(send_mail, db_user.email, body)
        except ConnectionErrors as e:
            # logger.error(e)
            raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=f'邮件发送失败')
    else:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)


# 前端代码应发送POST请求携带Token和新的密码
@router.post('/reset-password', status_code=status.HTTP_200_OK)
async def reset_password(token, password: str = Body(..., embed=True)):
    user_id = auth_handler.decode_token(token, audience=RESET_PASSWORD_AUDIENCE)
    db_user = await User.get_or_none(id=user_id)
    if db_user is None:  # 这里db_user是激活状态，不然用户不可能得到这个URL
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail='重置密码失败')

    new_hashed_password = auth_handler.get_password_hash(password)
    db_user.hashed_password = new_hashed_password
    try:
        await db_user.save(update_fields=['hashed_password'])
    except Exception as e:
        # loggine.error(e)
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail='密码重置失败')


# 个人主页，用户需要登录
@router.get('/me')
async def protected(user_id: int = Depends(auth_handler.auth_wrapper)):  # 依赖
    db_user = await User.get_or_none(id=user_id)
    if db_user is None:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail='用户不存在')
    return {'email': db_user.email, 'joined_date': db_user.joined_date.strftime('%Y-%m-%d')}

